/*
 * Copyright (c) 2021 Talkweb Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <math.h>
#include "cmsis_os2.h"
#include "motor.h"
#include "iot_errno.h"
#include "iot_gpio.h"
#include "iot_i2c.h"

#define E53_IA1_I2C_INDEX 0

void Gpio7IsrFunc(char *arg)
{
    (void)arg;

    printf("i2c create a isr\n");
    return;
}

static void MotorIoInit(void)
{
    IoTGpioInit(IOT_GPIO_IO_GPIO_8);
    IoTIoSetFunc(IOT_GPIO_IO_GPIO_8, IOT_IO_FUNC_GPIO_8_GPIO); //设置GPIO_8的复用功能为普通GPIO motor
    IoTGpioSetDir(IOT_GPIO_IO_GPIO_8, IOT_GPIO_DIR_OUT);      //设置GPIO_8为输出模式

    IoTGpioInit(IOT_GPIO_IO_GPIO_12);
    IoTIoSetFunc(IOT_GPIO_IO_GPIO_12, IOT_IO_FUNC_GPIO_12_GPIO); //设置GPIO_12的复用功能为普通GPIO led
    IoTGpioSetDir(IOT_GPIO_IO_GPIO_12, IOT_GPIO_DIR_OUT);       //设置GPIO_12为输出模式

    IoTGpioInit(IOT_GPIO_IO_GPIO_7);
    IoTIoSetFunc(IOT_GPIO_IO_GPIO_7, IOT_IO_FUNC_GPIO_7_GPIO); //设置GPIO_7的复用功能为普通GPIO，传感器中断输入
    IoTGpioSetDir(IOT_GPIO_IO_GPIO_7, IOT_GPIO_DIR_IN);       //设置GPIO_7为输入模式
    IoTIoSetPull(IOT_GPIO_IO_GPIO_7, IOT_IO_PULL_UP);
    IoTGpioRegisterIsrFunc(IOT_GPIO_IO_GPIO_7, IOT_INT_TYPE_EDGE, IOT_GPIO_EDGE_RISE_LEVEL_HIGH, Gpio7IsrFunc, NULL);

    IoTGpioInit(IOT_GPIO_IO_GPIO_13);
    IoTGpioInit(IOT_GPIO_IO_GPIO_14);
    IoTIoSetFunc(IOT_GPIO_IO_GPIO_13, IOT_IO_FUNC_GPIO_13_I2C0_SDA);
    IoTIoSetFunc(IOT_GPIO_IO_GPIO_14, IOT_IO_FUNC_GPIO_14_I2C0_SCL);

    IoTI2cInit(E53_IA1_I2C_INDEX, 400000); /* baudrate: 400kbps */
    IoTI2cSetBaudrate(E53_IA1_I2C_INDEX, 400000);
}

/**
 * @brief 初始化BH1750光照强度传感器
 */ 
static void InitBH1750(void)
{
    uint8_t send_data[1] = {0x01};
    IoTI2cWrite(E53_IA1_I2C_INDEX, (BH1750_Addr << 1) | 0x00, send_data, 1);
}

/**
 * @brief 启动BH1750光照强度传感器
 */ 
static void StartBH1750(void)
{
    uint8_t send_data[1] = {0x10};
    IoTI2cWrite(E53_IA1_I2C_INDEX, (BH1750_Addr << 1) | 0x00, send_data, 1);
}

/**
 * @brief 重置SHT30温湿度传感器状态
 */ 
static void SHT30Reset(void)
{
    uint8_t send_data[2] = {0x30, 0xA2};
    IoTI2cWrite(E53_IA1_I2C_INDEX, (SHT30_Addr << 1) | 0x00, send_data, 2);
}

/**
 * @brief 初始化SHT30温湿度传感器
 */ 
static void InitSHT30(void)
{
    uint8_t send_data[2] = {0x22, 0x36};
    IoTI2cWrite(E53_IA1_I2C_INDEX, (SHT30_Addr << 1) | 0x00, send_data, 2);
}

/**
 * @brief 检查温湿度传感器采集数据的有效性
 */ 
static uint8_t SHT3xCheckCrc(uint8_t data[], uint8_t nbrOfBytes, uint8_t checksum)
{

    uint8_t crc = 0xFF;
    uint8_t bit = 0;
    uint8_t byteCtr;
    const int16_t POLYNOMIAL = 0x131;

    for (byteCtr = 0; byteCtr < nbrOfBytes; ++byteCtr)
    {
        crc ^= (data[byteCtr]);
        for (bit = 8; bit > 0; --bit)
        {
            if (crc & 0x80)
                crc = (crc << 1) ^ POLYNOMIAL;
            else
                crc = (crc << 1);
        }
    }

    if (crc != checksum)
        return 1;
    else
        return 0;
}

/**
 * @brief 计算温度值
 */ 
static float SHT3xCalcTemperatureC(uint16_t u16sT)
{
    float temperatureC = 0; // variable for result
    u16sT &= ~0x0003;       // clear bits [1..0] (status bits)

    //-- calculate temperature [℃] --
    //T = -45 + 175 * rawValue / (2^16-1)
    temperatureC = (175 * (float)u16sT / 65535 - 45); 

    return temperatureC;
}

/**
 * @brief 计算湿度值
 */ 
static float SHT3xCalcRH(uint16_t u16sRH)
{

    float humidityRH = 0; // variable for result
    u16sRH &= ~0x0003;    // clear bits [1..0] (status bits)

    //-- calculate relative humidity [%RH] --
    // RH = rawValue / (2^16-1) * 10
    humidityRH = (100 * (float)u16sRH / 65535); 

    return humidityRH;
}

/**
 * @brief 初始化马达开发板的硬件状态
 */ 
void MotorInit(void)
{
    MotorIoInit();
    InitBH1750();
    InitSHT30();
}

/**
 * @brief 读取光照强度传感器和温湿度传感器的数据
 */
void MotorReadData(Motor_Data_TypeDef *motor_data)
{
    // 启动传感器采集数据
    StartBH1750();
    usleep(180000);

    uint8_t recv_data[2] = {0};
    uint32_t recv_len = 2;
    IoTI2cRead(E53_IA1_I2C_INDEX, (BH1750_Addr << 1) | 0x01, recv_data, recv_len);
    motor_data->Lux = (float)(((recv_data[0] << 8) + recv_data[1]) / 1.2);

    uint8_t data[3]; //data array for checksum verification
    uint16_t dat, tmp;
    uint8_t SHT3X_Data_Buffer[6]; //byte 0, 1 is temperature; byte 4, 5 is humidity
    uint8_t send_data[2] = {0xE0, 0x00};
    IotI2cData wData = {0};
    wData.sendBuf = send_data;
    wData.sendLen = 2;
    wData.receiveBuf = SHT3X_Data_Buffer;
    wData.receiveLen = 6;
    IoTI2cWriteread(E53_IA1_I2C_INDEX, (SHT30_Addr << 1) | 0x00, &wData);
    data[0] = SHT3X_Data_Buffer[0];
    data[1] = SHT3X_Data_Buffer[1];
    data[2] = SHT3X_Data_Buffer[2];

    tmp = SHT3xCheckCrc(data, 2, data[2]);
    if (!tmp) // value is true
    {
        dat = ((uint16_t)data[0] << 8) | data[1];
        motor_data->Temperature = SHT3xCalcTemperatureC(dat);
    }

    //check humidity
    data[0] = SHT3X_Data_Buffer[3];
    data[1] = SHT3X_Data_Buffer[4];
    data[2] = SHT3X_Data_Buffer[5];

    tmp = SHT3xCheckCrc(data, 2, data[2]);
    if (!tmp) // value is true
    {
        dat = ((uint16_t)data[0] << 8) | data[1];
        motor_data->Humidity = SHT3xCalcRH(dat);
    }
}

/**
 * @brief 设置LED灯的开关状态
 */
void LightStatusSet(Motor_Status_ENUM status)
{
    if (status == ON)
        IoTGpioSetOutputVal(IOT_GPIO_IO_GPIO_12, 0); //设置GPIO_12输出低电平点亮灯
    if (status == OFF)
        IoTGpioSetOutputVal(IOT_GPIO_IO_GPIO_12, 1); //设置GPIO_12输出高电平关闭灯
}

/**
 * @brief 设置马达的开关状态
 */
void MotorStatusSet(Motor_Status_ENUM status)
{
    if (status == ON)
        IoTGpioSetOutputVal(IOT_GPIO_IO_GPIO_8, 1); //设置GPIO_8输出高电平打开电机
    if (status == OFF)
        IoTGpioSetOutputVal(IOT_GPIO_IO_GPIO_8, 0); //设置GPIO_8输出低电平关闭电机
}
